package ru.volgogradetzzz.text 
{
	import flash.display.Graphics;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.text.engine.BreakOpportunity;
	import flash.text.engine.ElementFormat;
	import flash.text.engine.FontDescription;
	import flash.text.engine.FontLookup;
	import flash.text.engine.FontPosture;
	import flash.text.engine.FontWeight;
	import flash.text.engine.RenderingMode;
	import flash.text.engine.TextBaseline;
	import flash.text.engine.TextBlock;
	import flash.text.engine.TextElement;
	import flash.text.engine.TextLine;
	import flash.ui.Mouse;
	import flash.ui.MouseCursor;
	import flash.utils.getQualifiedClassName;
	
	/**
	 * VMultilineText can contain multiply text lines. Text can set it's width automatically if autoWidth set to true,
	 * and height if autoHeight set to true. In other cases text is wraps or trunkates if string width is greater then
	 * explicitly setted width or string height is greater then explicitly setted height. Text can be formatted.
	 * Whole text change it's style when new format applied.
	 * 
	 * @author Volgogradetzzz
	 */
	public class VMultilineText extends Sprite implements IFTEVText
	{
		//--------------------------------------
		// Private fields
		//--------------------------------------
		
		/**
		 * Auxiliary property.
		 */
		private var _textElement:TextElement = new TextElement();
		/**
		 * Indicates that text control can be selected or not.
		 */
		private var _selectable:Boolean = false;
		/**
		 * Holds atom index.
		 */
		private var _startInd:int = -1;
		/**
		 * Holds atom center respective to mouse position.
		 */
		private var _startMouseAtomPos:String;
		/**
		 * Mouse position in the place of click.
		 */
		private var _startMousePosX:Number;
		/**
		 * Atom info object that contains atom center respective to mouse position and atom index.
		 */
		private var _atomInfo:Object = new Object();
		/**
		 * Start index of selection.
		 */
		private var _startIndForClipboard:int = -1;
		/**
		 * End index of selection.
		 */
		private var _endIndForClipboard:int = -1;
		
		//--------------------------------------
		// Protected fields
		//--------------------------------------
		
		/**
		 * Text to display.
		 */
		protected var _text:String = '';
		/**
		 * Auxiliary property.
		 */
		protected var _block:TextBlock = new TextBlock(_textElement);
		/**
		 * Delays control updating until 'update' called manually.
		 */
		protected var _updateLater:Boolean = false;
		/**
		 * Text format.
		 */
		protected var _textFormat:VTextFormat = new VTextFormat();
		/**
		 * Hold text lines
		 */
		protected var _linesCont:Sprite = new Sprite();
		/**
		 * Array of lines.
		 */
		protected var _allLines:Vector.<TextLine> = new Vector.<TextLine>();
		/**
		 * Shape for correct width/height getting
		 */
		protected var _bg:Shape = new Shape();
		/**
		 * Text control width.
		 */
		protected var _w:Number = 100;
		/**
		 * Text control height.
		 */
		protected var _h:Number = 100;
		/**
		 * Allows to VMultilineText instance to 'grow' or 'shrink' automatically in horizontal direction.
		 */
		protected var _autoWidth:Boolean = false;
		/**
		 * Allows to VMultilineText instance to 'grow' or 'shrink' automatically in vertical direction.
		 */
		protected var _autoHeight:Boolean = false;
		/**
		 * Total atoms in text control. Can be less than supplied string because text can be trunkated.
		 */
		protected var _totalAtoms:int;
		
		//--------------------------------------
		// Constructor
		//--------------------------------------
		
		/**
		 * Creates new instance of VMultilineText.
		 */
		public function VMultilineText() 
		{
			super();
			
			var g:Graphics = _bg.graphics;
			g.beginFill(0x000000, 0.0);
			g.drawRect(0, 0, 100, 100);
			g.endFill();
			_bg.width = _w;
			_bg.height = _h;
			addChild(_bg);
			
			addChild(_linesCont);
			
			doubleClickEnabled = true;
			
			text = _text;
			
			addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
			addEventListener(Event.REMOVED_FROM_STAGE, removedFromStageHandler);
		}
		
		//--------------------------------------
		// Public methods
		//--------------------------------------
		
		/**
		 * Method recreates lines and set 'updateLater' property to false.
		 * You should call it if 'updateLater' was set to 'true'.
		 */
		public function update():void
		{
			_updateLater = false;
			createLines();
		}
		
		/**
		 * Sets selection in specified interval.
		 * @param	startInd	Start index of selection
		 * @param	endInt		End index of selection.
		 */
		public function setSelection(startInd:int, endInt:int):void
		{
			_startMouseAtomPos = 'left';
			drawSelection(startInd, endInt)
		}
		
		/**
		 * Removes any selection.
		 */
		public function clearSelection():void
		{
			graphics.clear();
			_startIndForClipboard = -1;
			_endIndForClipboard = -1;
		}
		
		//--------------------------------------
		// Public properties
		//--------------------------------------
		
		/**
		 * Gets start index of selection. If no selection made -1 returned.
		 */
		public function get selectionBeginIndex():int
		{
			return _startIndForClipboard;
		}
		
		/**
		 * Gets end index of selection. If no selection made -1 returned.
		 */
		public function get selectionEndIndex():int
		{
			return _endIndForClipboard;
		}
		
		/**
		 * Gets or sets text message for VMultilineText instance. VMultilineText can contain multiply text lines.
		 * If width is not enougth to hold the whole string then string is wraps. If height is not enougth to hold the
		 * whole string then string is trunkates and property returns trunkated text.
		 * To get full text use 'fullText' property.
		 */
		public function get text():String { return _text.substr(0, _totalAtoms); }
		/**
		 * @private
		 */
		public function set text(value:String):void
		{
			_text = value;
			
			var fontWeight:String = _textFormat.bold ? FontWeight.BOLD : FontWeight.NORMAL;
			var fontStyle:String = _textFormat.italic ? FontPosture.ITALIC : FontPosture.NORMAL;
			var embedFont:String = _textFormat.embedFont ? FontLookup.EMBEDDED_CFF : FontLookup.DEVICE;
			
			var fontDesc:FontDescription = new FontDescription(_textFormat.font, fontWeight, fontStyle, embedFont, RenderingMode.NORMAL);
			var format:ElementFormat = new ElementFormat(fontDesc, _textFormat.size, _textFormat.color);
			format.breakOpportunity = BreakOpportunity.AUTO;
			
			_textElement.text = _text;
			_textElement.elementFormat = format;
			
			if(!_updateLater) createLines();
		}
		
		/**
		 * Returns full text. If 'autoWidth' is true then 'text' and 'fullText' are same.
		 */
		public function get fullText():String { return _text };
		
		/**
		 * Gets or sets width of VMultilineText instance. If autoWidth is true then setting width nave no effect
		 * and getting width returns actual width - not width that was setted explicitly. So to set width be sure
		 * to set false to autoWidth property.
		 */
		override public function get width():Number { return _w; }
		/**
		 * @private
		 */
		override public function set width(value:Number):void 
		{
			_w = value < 0 ? 0 : value;
			
			if(!_updateLater) createLines();
		}
		
		/**
		 * Gets or sets height of VMultilineText instance. If autoHeight is true then setting height nave no effect
		 * and getting height returns actual height - not height that was setted explicitly. So to set height be sure
		 * to set false to autoHeight property.
		 */
		override public function get height():Number { return _h; }
		/**
		 * @private
		 */
		override public function set height(value:Number):void 
		{
			_h = value < 0 ? 0 : value;
			
			if(!_updateLater) createLines();
		}
		
		/**
		 * Allows to VMultilineText instance to 'grow' or 'shrink' automatically in horizontal direction
		 * when new text entered.
		 */
		public function get autoWidth():Boolean { return _autoWidth; }
		/**
		 * @private
		 */
		public function set autoWidth(value:Boolean):void 
		{
			_autoWidth = value;
			
			if(!_updateLater) createLines();
		}
		
		/**
		 * Allows to VMultilineText instance to 'grow' or 'shrink' automatically in vertical direction
		 * when new text entered.
		 */
		public function get autoHeight():Boolean { return _autoHeight; }
		/**
		 * @private
		 */
		public function set autoHeight(value:Boolean):void 
		{
			_autoHeight = value;
			
			if(!_updateLater) createLines();
		}
		
		/**
		 * Sets new text format. VMultilineText supports align, bold, italic, color, font, size and embedFont properties.
		 */
		public function get textFormat():VTextFormat { return _textFormat; }
		/**
		 * @private
		 */
		public function set textFormat(value:VTextFormat):void
		{
			_textFormat = value;
			text = _text;
		}
		
		/**
		 * Most of properties of VMultilineText cause to recreate lines when that property change. For example,
		 * you can change 'width' and 'height' and for every property lines would be recreated. But  it would be better
		 * to commit all that properties at once and update control only one time. You can do it by setting 'updateLater'
		 * to 'true', changing neccesary properties and than calling 'update' method.
		 */
		public function get updateLater():Boolean { return _updateLater; }
		/**
		 * @private
		 */
		public function set updateLater(value:Boolean):void 
		{
			_updateLater = value;
		}
		
		/**
		 * Indicates that text control can be selected or not.
		 */
		public function get selectable():Boolean { return _selectable; }
		/**
		 * @private
		 */
		public function set selectable(value:Boolean):void 
		{
			_selectable = value;
		}
		
		/**
		 * Gets characters number in VMultilineText instance. If width is not enougth to hold whole string
		 * then string is trunkates and property returns trunkated text length.
		 * To get full text length use 'fullLength' property.
		 */
		public function get length():int { return _totalAtoms };
		
		/**
		 * Gets characters number of full text in VMultilineText instance. If 'autoWidth' is true
		 * then 'length' and 'fullLength' are same.
		 */
		public function get fullLength():int { return _text.length };
		
		//--------------------------------------
		// Private methods
		//--------------------------------------
		
		/**
		 * @private Create lines and aligns them horizontally and vertically.
		 */
		protected function createLines():void
		{
			_allLines.length = 0;
			_totalAtoms = 0;
			
			//if lines exists - remove them.
			var numLines:int = _linesCont.numChildren;
			while (--numLines > -1)
			{
				_linesCont.removeChildAt(numLines);
			}
			
			//if text was created before - release it's data for garbage collection.
			if (_block)
			{
				var firstLine:TextLine = _block.firstLine;
				var lastLine:TextLine = _block.lastLine;
				if (firstLine) _block.releaseLines(firstLine, lastLine);
			}
			
			var w:Number = _autoWidth ? 1000000 : _w;
			var line:TextLine = _block.createTextLine(null, w);
			var lastLineDescent:Number = 0;
			
			var isFirstLineFit:Boolean = true;
			
			if (line)
			{
				line.y = line.height;
				lastLineDescent = Math.ceil(line.getBaselinePosition(TextBaseline.DESCENT));
				line.doubleClickEnabled = true;
				
				if (_autoHeight)
				{
					_linesCont.addChild(line);
					_allLines.push(line);
					_totalAtoms += line.atomCount;
				}
				else
				{
					if (line.y + lastLineDescent < _h)
					{
						_linesCont.addChild(line);
						_allLines.push(line);
						_totalAtoms += line.atomCount;
					}
					else
					{
						isFirstLineFit = false;
					}
				}
			}
			
			while (line && isFirstLineFit)
			{
				line = _block.createTextLine(line, w);
				
				if (line)
				{
					line.y = _linesCont.height + line.height + _textFormat.lineSpace;
					lastLineDescent = Math.ceil(line.getBaselinePosition(TextBaseline.DESCENT));
					line.doubleClickEnabled = true;
					
					if (_autoHeight)
					{
						_linesCont.addChild(line);
						_allLines.push(line);
						_totalAtoms += line.atomCount;
					}
					else
					{
						if (line.y + lastLineDescent < _h)
						{
							_linesCont.addChild(line);
							_allLines.push(line);
							_totalAtoms += line.atomCount;
						}
						else
						{
							break;
						}
					}
				}
			}
			
			if (_autoWidth)
			{
				_w = _linesCont.width;
			}
			
			if (_autoHeight)
			{
				_h = _linesCont.height + lastLineDescent;
			}
			
			numLines = _linesCont.numChildren;
			if (_textFormat.horizontalAlign == VTextAlign.LEFT)
			{
				while (--numLines > -1)
				{
					line = _linesCont.getChildAt(numLines) as TextLine;
					line.x = 0;
				}
			}
			else if (_textFormat.horizontalAlign == VTextAlign.CENTER)
			{
				while (--numLines > -1)
				{
					line = _linesCont.getChildAt(numLines) as TextLine;
					line.x = (_w - line.width) >> 1;
				}
			}
			else if (_textFormat.horizontalAlign == VTextAlign.RIGHT)
			{
				while (--numLines > -1)
				{
					line = _linesCont.getChildAt(numLines) as TextLine;
					line.x = _w - line.width;
				}
			}
			
			numLines = _linesCont.numChildren;
			if (_textFormat.verticalAlign == VTextAlign.TOP)
			{
				for (var i:int = 0; i < numLines; i++)
				{
					line = _linesCont.getChildAt(i) as TextLine;
					line.y = i == 0 ? line.height : _linesCont.getChildAt(i - 1).y + line.height + _textFormat.lineSpace;
				}
			}
			else if (_textFormat.verticalAlign == VTextAlign.MIDDLE)
			{
				for (i = 0; i < numLines; i++)
				{
					line = _linesCont.getChildAt(i) as TextLine;
					line.y = i == 0 ? ((_h - _linesCont.height - lastLineDescent) >> 1) + line.height : _linesCont.getChildAt(i - 1).y + line.height + _textFormat.lineSpace;
				}
			}
			else if (_textFormat.verticalAlign == VTextAlign.BOTTOM)
			{
				for (i = numLines - 1; i > -1; i--)
				{
					line = _linesCont.getChildAt(i) as TextLine;
					line.y = i == numLines - 1 ? _h - lastLineDescent : _linesCont.getChildAt(i + 1).y - line.height - _textFormat.lineSpace;
				}
			}
			
			_bg.width = _w;
			_bg.height = _h;
		}
		
		/**
		 * Adds nesessary listeners if instance added to stage.
		 * @param	event
		 */
		private function addedToStageHandler(event:Event = null):void 
		{
			addEventListener(MouseEvent.MOUSE_OVER, mouseOverHandler);
			addEventListener(MouseEvent.MOUSE_OUT, mouseOutHandler);
			addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
			addEventListener(MouseEvent.DOUBLE_CLICK, doubleClickHandler);
			stage.addEventListener(MouseEvent.MOUSE_UP, stageMouseUpHandler);
			stage.addEventListener(MouseEvent.MOUSE_DOWN, stageMouseDownHandler);
		}
		
		/**
		 * Removes listeners if instance removed from stage.
		 * @param	event
		 */
		private function removedFromStageHandler(event:Event = null):void 
		{
			removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
			
			removeEventListener(MouseEvent.MOUSE_OVER, mouseOverHandler);
			removeEventListener(MouseEvent.MOUSE_OUT, mouseOutHandler);
			removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
			removeEventListener(MouseEvent.DOUBLE_CLICK, doubleClickHandler);
			stage.removeEventListener(MouseEvent.MOUSE_UP, stageMouseUpHandler);
			stage.removeEventListener(MouseEvent.MOUSE_DOWN, stageMouseDownHandler);
		}
		
		/**
		 * Handles mouse over.
		 * @param	event
		 */
		private function mouseOverHandler(event:MouseEvent = null):void 
		{
			if(_selectable) Mouse.cursor = MouseCursor.IBEAM;
		}
		
		/**
		 * Handles mouse out.
		 * @param	event
		 */
		private function mouseOutHandler(event:MouseEvent = null):void 
		{
			if(_selectable) Mouse.cursor = MouseCursor.ARROW;
		}
		
		/**
		 * Handles mouse down.
		 * @param	event
		 */
		private function mouseDownHandler(event:MouseEvent = null):void 
		{
			clearSelection();
			
			if (!_selectable) return;
			
			if (_text.length == 0) return;
			
			var atomInfo:Object = getIndexUnderMouse();
			
			_startMousePosX = stage.mouseX;
			_startInd = atomInfo.index;
			_startMouseAtomPos = atomInfo.mouseAtomPos;
			
			addEventListener(Event.ENTER_FRAME, enterFrameHandler);
		}
		
		/**
		 * Handles double click - selects word under mouse.
		 * @param	event
		 */
		private function doubleClickHandler(event:MouseEvent = null):void 
		{
			if (!_selectable) return;
			
			if (_text.length == 0) return;
			
			var atomInfo:Object = getIndexUnderMouse();
			var indInBlock:int = atomInfo.index;
			var indInLine:int = indInBlock - atomInfo.textLine.textBlockBeginIndex;
			
			var selectedChar:String = _text.substr(indInBlock, 1);
			if (selectedChar == ' ' || selectedChar == '\n')
			{
				var wordStartInd:int = indInBlock;
				var wordEndInd:int = indInBlock - 1;
				
				for (var i:int = indInBlock - 1; i > -1; i--)
				{
					selectedChar = _text.substr(i, 1);
					if (selectedChar != ' ' && selectedChar != '\n')
					{
						break;
					}
					else
					{
						wordStartInd = i;
					}
				}
				
				for (i = indInBlock; i < _totalAtoms; i++)
				{
					selectedChar = _text.substr(i, 1);
					if (selectedChar != ' ' && selectedChar != '\n')
					{
						break;
					}
					else
					{
						wordEndInd = i;
					}
				}
			}
			else if(atomInfo.textLine.getAtomWordBoundaryOnLeft(indInLine))
			{
				wordStartInd = indInBlock;
				wordEndInd = _block.findNextWordBoundary(indInBlock) - 1;
				
				if (wordEndInd > _totalAtoms - 1)
				{
					wordEndInd = _totalAtoms - 1;
				}
				
				for (i = wordEndInd + 1; i < _totalAtoms; i++)
				{
					selectedChar = _text.substr(i, 1);
					if (selectedChar != ' ' && selectedChar != '\n')
					{
						break;
					}
					else
					{
						wordEndInd = i;
					}
				}
			}
			else
			{
				wordStartInd = _block.findPreviousWordBoundary(indInBlock);
				wordEndInd = _block.findNextWordBoundary(indInBlock) - 1;
				
				if (wordEndInd > _totalAtoms - 1)
				{
					wordEndInd = _totalAtoms - 1;
				}
				
				for (i = wordEndInd + 1; i < _totalAtoms; i++)
				{
					selectedChar = _text.substr(i, 1);
					if (selectedChar != ' ' && selectedChar != '\n')
					{
						break;
					}
					else
					{
						wordEndInd = i;
					}
				}
			}
			
			_startMouseAtomPos = 'left';
			//if (wordEndInd == _totalAtoms) wordEndInd--;
			
			drawSelection(wordStartInd, wordEndInd);
		}
		
		/**
		 * Handles stage mouse up.
		 * @param	event
		 */
		private function stageMouseUpHandler(event:MouseEvent = null):void 
		{
			removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
		}
		
		/**
		 * Handles stage mouse down. Removes any selection.
		 * @param	event
		 */
		private function stageMouseDownHandler(event:MouseEvent = null):void 
		{
			clearSelection();
		}
		
		/**
		 * Gets atom index under mouse. Lines are choses based on mouse Y position.
		 * If mouse not over line - nearest up line selects.
		 * If mouse above all lines - first line selects.
		 * @return
		 */
		private function getIndexUnderMouse():Object
		{
			var upLine:TextLine;
			
			var atomInfo:Object = new Object();
			atomInfo.index = -1;
			
			var numLines:int = _allLines.length;
			for (var i:int = 0; i < numLines; i++)
			{
				var line:TextLine = _allLines[i];
				
				if (line.mouseY >= -line.height && line.mouseY <= 0)
				{
					atomInfo = getIndexAtLine(line);
					atomInfo.textLine = line;
					break;
				}
				
				if (this.mouseY > line.y)
				{
					upLine = line;
				}
			}
			
			if (atomInfo.index == -1)
			{
				if (upLine)
				{
					atomInfo = getIndexAtLine(upLine);
					atomInfo.textLine = upLine;
				}
				else
				{
					atomInfo = getIndexAtLine(_allLines[0]);
					atomInfo.textLine = _allLines[0];
				}
			}
			
			return atomInfo;
		}
		
		/**
		 * Gets atom index in line based on mouse X position.
		 * If mouse is on the right of line - last index selects.
		 * If mouse is on the left of line - first index selects.
		 * @param	line Line to select index from.
		 * @return
		 */
		private function getIndexAtLine(line:TextLine):Object
		{
			var atomInfo:Object = new Object();
			
			var lineLocalPoint:Point = new Point(line.mouseX, 0);
			var lineGlobalPoint:Point = line.localToGlobal(lineLocalPoint);
			var lastAtomBounds:Rectangle = line.getAtomBounds(line.atomCount - 1);
			
			if (this.mouseX >= line.x + lastAtomBounds.x + lastAtomBounds.width)
			{
				atomInfo.index = line.textBlockBeginIndex + line.atomCount - 1;
				atomInfo.mouseAtomPos = 'right';
			}
			else if (this.mouseX <= line.x)
			{
				atomInfo.index = line.textBlockBeginIndex;
				atomInfo.mouseAtomPos = 'left';
			}
			else
			{
				var indInLine:int = line.getAtomIndexAtPoint(lineGlobalPoint.x, lineGlobalPoint.y);
				atomInfo.index = line.textBlockBeginIndex + indInLine;
				
				if (line.getAtomCenter(indInLine) > line.mouseX)
				{
					atomInfo.mouseAtomPos = 'left';
				}
				else
				{
					atomInfo.mouseAtomPos = 'right';
				}
			}
			
			return atomInfo;
		}
		
		/**
		 * Manages selection drawing.
		 * @param	event
		 */
		private function enterFrameHandler(event:Event = null):void 
		{
			var prevEndInd:int = _atomInfo.index;
			var prevEndMouseAtomPos:String = _atomInfo.mouseAtomPos;
			
			_atomInfo = getIndexUnderMouse();
			var endInd:int = _atomInfo.index;
			var endMouseAtomPos:String = _atomInfo.mouseAtomPos;
			
			if (endInd == prevEndInd && endMouseAtomPos == prevEndMouseAtomPos) return;
			
			if (_startInd == endInd)
			{
				if (_startMouseAtomPos != endMouseAtomPos)
				{
					drawSelection(_startInd, endInd);
				}
				else
				{
					clearSelection();
				}
			}
			else
			{
				if (_startMouseAtomPos == 'right')
				{
					if (_startInd + 1 < _totalAtoms)
					{
						drawSelection(_startInd, endInd);
					}
					else
					{
						drawSelection(_startInd, endInd);
					}
				}
				else
				{
					drawSelection(_startInd, endInd);
				}
			}
		}
		
		/**
		 * Draws selection based on incoming first and last indicies.
		 * @param	startInd
		 * @param	endInd
		 */
		private function drawSelection(startInd:int, endInd:int):void
        {
			clearSelection();
			
			if (startInd < endInd)
			{
				_startIndForClipboard = startInd;
				_endIndForClipboard = endInd;
			}
			else
			{
				_startIndForClipboard = endInd;
				_endIndForClipboard = startInd;
			}
            
			var lineToStartFrom:TextLine = _block.getTextLineAtCharIndex(startInd);
			var lineToEndTo:TextLine = _block.getTextLineAtCharIndex(endInd);
			
			var start:int = startInd - lineToStartFrom.textBlockBeginIndex;
			var end:int = endInd - lineToEndTo.textBlockBeginIndex;
			
			var startBounds:Rectangle = lineToStartFrom.getAtomBounds(start);
			var endBounds:Rectangle = lineToEndTo.getAtomBounds(end);
			
			var rects:Vector.<Rectangle> = new Vector.<Rectangle>();
			var rect:Rectangle = new Rectangle();
			
			if (lineToStartFrom == lineToEndTo)
			{
				if (start == end)
				{
					rect.x = lineToStartFrom.x + startBounds.x;
					rect.y = lineToStartFrom.y + startBounds.y;
					rect.width = endBounds.x + endBounds.width - startBounds.x;
					rect.height = lineToStartFrom.height;
				}
				else if (start < end)
				{
					rect.x = lineToStartFrom.x + startBounds.x;
					rect.y = lineToStartFrom.y + startBounds.y;
					rect.width = endBounds.x + endBounds.width - startBounds.x;
					rect.height = lineToStartFrom.height;
					
					if (_startMouseAtomPos == 'right')
					{
						rect.x += startBounds.width;
						rect.width -= startBounds.width;
					}
				}
				else
				{
					rect.x = lineToStartFrom.x + startBounds.x;
					rect.y = lineToStartFrom.y + startBounds.y;
					rect.width = endBounds.x - startBounds.x;
					rect.height = lineToStartFrom.height;
					
					if (_startMouseAtomPos == 'right')
					{
						rect.x += startBounds.width;
						rect.width -= startBounds.width;
					}
				}
				
				rects.push(rect);
			}
			else
			{
				if (startInd < endInd)
				{
					var startRect:Rectangle = new Rectangle();
					var lastCharBounds:Rectangle = lineToStartFrom.getAtomBounds(lineToStartFrom.atomCount - 1);
					
					startRect.x = lineToStartFrom.x + startBounds.x;
					startRect.y = lineToStartFrom.y + startBounds.y;
					startRect.width = lastCharBounds.x + lastCharBounds.width - startBounds.x;
					startRect.height = lineToStartFrom.height;
					
					if (_startMouseAtomPos == 'right')
					{
						startRect.x += startBounds.width;
						startRect.width -= startBounds.width;
					}
					
					rects.push(startRect);
					
					//do middle lines
					var startLineInd:int = _allLines.indexOf(lineToStartFrom);
					var endLineInd:int = _allLines.indexOf(lineToEndTo);
					for (var i:int = startLineInd + 1; i < endLineInd; i++)
					{
						var midLine:TextLine = _allLines[i];
						var midLineFirstCharBounds:Rectangle = midLine.getAtomBounds(0);
						var midLineLastCharBounds:Rectangle = midLine.getAtomBounds(midLine.atomCount - 1);
						var midRect:Rectangle = new Rectangle();
						midRect.x = midLine.x + midLineFirstCharBounds.x;
						midRect.y = midLine.y + midLineFirstCharBounds.y;
						midRect.width = midLineLastCharBounds.x + midLineLastCharBounds.width - midLineFirstCharBounds.x;
						midRect.height = midLine.height;
						
						rects.push(midRect);
					}
					
					var endRect:Rectangle = new Rectangle();
					var firstCharBounds:Rectangle = lineToEndTo.getAtomBounds(0);
					
					endRect.x = lineToEndTo.x + firstCharBounds.x;
					endRect.y = lineToEndTo.y + firstCharBounds.y;
					endRect.width = endBounds.x + endBounds.width - firstCharBounds.x;
					endRect.height = lineToEndTo.height;
					
					
					rects.push(endRect);
				}
				else
				{
					startRect = new Rectangle();
					firstCharBounds = lineToStartFrom.getAtomBounds(0);
					
					startRect.x = lineToStartFrom.x + startBounds.x;
					startRect.y = lineToStartFrom.y + startBounds.y;
					startRect.width = firstCharBounds.x - startBounds.x;
					startRect.height = lineToStartFrom.height;
					
					if (_startMouseAtomPos == 'right')
					{
						startRect.x += startBounds.width;
						startRect.width -= startBounds.width;
					}
					
					rects.push(startRect);
					
					//do middle lines
					startLineInd = _allLines.indexOf(lineToStartFrom);
					endLineInd = _allLines.indexOf(lineToEndTo);
					for (i = startLineInd - 1; i > endLineInd; i--)
					{
						midLine = _allLines[i];
						midLineFirstCharBounds = midLine.getAtomBounds(0);
						midLineLastCharBounds = midLine.getAtomBounds(midLine.atomCount - 1);
						midRect = new Rectangle();
						midRect.x = midLine.x + midLineFirstCharBounds.x;
						midRect.y = midLine.y + midLineFirstCharBounds.y;
						midRect.width = midLineLastCharBounds.x + midLineLastCharBounds.width - midLineFirstCharBounds.x;
						midRect.height = midLine.height;
						
						rects.push(midRect);
					}
					
					endRect = new Rectangle();
					lastCharBounds = lineToEndTo.getAtomBounds(lineToEndTo.atomCount - 1);
					
					endRect.x = lineToEndTo.x + endBounds.x;
					endRect.y = lineToEndTo.y + endBounds.y;
					endRect.width = lastCharBounds.x + lastCharBounds.width - endBounds.x;
					endRect.height = lineToEndTo.height;
					
					rects.push(endRect);
				}
			}
			
			for each(rect in rects)
			{
				graphics.beginFill(0x003399, 0.25);
				graphics.drawRect(rect.x, rect.y, rect.width, rect.height);
				graphics.endFill();
			}
        }
	}
}